/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package argtrust.xml;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.*;


import java.lang.System;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.io.File;

import aima.core.logic.fol.parsing.FOLParser;
import aima.core.logic.fol.parsing.ast.Sentence;
import aima.core.logic.fol.domain.FOLDomain;
import aima.core.logic.fol.Connectors;

import argtrust.logic.FOLNaturalDeduction;
import argtrust.logic.FOLKnowledgeBase;
import argtrust.logic.InferenceRule;
import argtrust.logic.InferenceNode;
import argtrust.argsys.ArgumentationSystem;
import argtrust.argsys.ArguingNode;
import argtrust.argsys.DefeatRule;
import argtrust.belief.BeliefBase;
import argtrust.trustnet.Agent;
import argtrust.trustnet.TrustNet;
import argtrust.trustnet.TrustNode;
import argtrust.tidal.TidalTrust;
import argtrust.tidal.TidalTrustBelief;
import argtrust.trustnet.ITrustSemantics;
import argtrust.measure.Measure;
import argtrust.measure.LevelMeasure;
import argtrust.certprop.CertPropMeasure;

import argtrust.argtrustgraph.ArgTrustGraph;

/**
 *
 * @author Yuqing Tang
 */
public class XMLTrustBeliefReader {

    FOLDomain mDomain;
    TrustNet mTrustnet;
    BeliefBase mBeliefBase;
    FOLParser mFolParser;
    //TODO: Add to BeliefBase
    List<DefeatRule> _DefeatRuleList = new ArrayList<DefeatRule>();

//    ArgumentationSystem mArgSys;
    public XMLTrustBeliefReader() {
        mDomain = new FOLDomain();
        mTrustnet = new TrustNet();
        mBeliefBase = new BeliefBase();
        mFolParser = new FOLParser(mDomain);
//        mArgSys = new ArgumentationSystem();

    }

    public FOLParser getFolParser() {
        return mFolParser;
    }

    public BeliefBase getBeliefBase() {
        return mBeliefBase;
    }

    public TrustNet getTrustnet() {
        return mTrustnet;
    }

    public List<DefeatRule> getDefeatRuleList() {
        return _DefeatRuleList;
    }

    public void setDefeatRuleList(List<DefeatRule> _DefeatRuleList) {
        this._DefeatRuleList = _DefeatRuleList;
    }

    public void readFile(String file_name) {
        readFile(new File(file_name));
    }
    public void readFile(List<File> input_file_list) {
        for (File file: input_file_list)
        {
            readFile(file);
        }
    }
    public void readFile(File input_file) {
        DOMParser p = new DOMParser();

        try {
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(input_file);

//            Node n = doc.getFirstChild();
            Node n = doc.getDocumentElement().getFirstChild();
            while (n != null) {
                process(n);
                //Get next sibling node
                n = n.getNextSibling();
            }
        } catch (Exception e) {

            e.printStackTrace();
        }
    }

    void addConstant(Node node) {
        mDomain.addConstant(getNodeTextValue(node));
    }

    void addPredicate(Node node) {
        mDomain.addPredicate(getNodeTextValue(node));
    }

    Agent getAgent(Node node) {
        return mTrustnet.getAgent(getNodeTextValue(node));
    }
    void processIncludeFile(Node node) {
        readFile(getNodeTextValue(node));
    }

    double getDouble(Node node) {
        return Double.parseDouble(getNodeTextValue(node));
    }

    Measure getCertPropMeasure(Node node) {
        Node n = node.getFirstChild();
        Measure measure = null;
        double r = 0;
        double s = 0;
        while (n != null) {
            if (n.getNodeName().equals("r")) {
                r = getDouble(n);
            } else if (n.getNodeName().equals("s")) {
                s = getDouble(n);
            }

            //Get next sibling node
            n = n.getNextSibling();
        }
        measure = new CertPropMeasure(r, s);
        return measure;
    }

    Measure getMeasure(Node node) {
        Measure measure = null;
        String node_name = node.getNodeName();
        if (node_name.equals("level")) {
            double level = getDouble(node);
            measure = new LevelMeasure(level);
        } else if (node_name.equals("certprop")) {
            measure = getCertPropMeasure(node);
        }
        return measure;
    }

    Measure getTrustMeasure(Node node) {
        //TODO: return the different kind of measures:
        return new LevelMeasure(Double.parseDouble(getNodeTextValue(node)));
    }

    Agent getTrustee(Node node) {
        return getAgent(node);
    }

    Agent getTruster(Node node) {
        return getAgent(node);
    }

    void addTrust(Node node) {
        Node n = node.getFirstChild();
        Agent truster = null;
        Agent trustee = null;
        Measure measure = null;
        while (n != null) {
            String node_name = n.getNodeName();
            if (node_name.equals("truster")) {
                truster = getTruster(n);
            } else if (node_name.equals("trustee")) {
                trustee = getTrustee(n);
            } else if (node_name.equals("level")
                    || node_name.equals("certprop")) {
                measure = getMeasure(n);
            }

            //Get next sibling node
            n = n.getNextSibling();
        }
        if (measure == null) {
            System.out.println("No measure specified between "
                    + truster.getName()
                    + " and "
                    + trustee.getName());
            System.exit(255);
        }
        mTrustnet.trust(truster, trustee, measure);

    }

    Sentence getSentence(Node node) {
//        System.out.println("parsing sentence for:");
//        print(node);
//        System.out.println("text: " + getNodeTextValue(node));
        try {
            return mFolParser.parse(getNodeTextValue(node));
        } catch (RuntimeException e) {
            System.out.println("Errors in parsing: " + getNodeTextValue(node));
            throw e;
        }
    }

    Sentence getFact(Node node) {
        return getSentence(node);
    }

    DefeatRule getDefeatRule(Node node) {
        DefeatRule res = new DefeatRule();
        Node n = node.getFirstChild();
        while (n != null) {
            if (n.getNodeName().equals("defeatee")) {
                res.setDefeatee(getSentence(n));
            } else if (n.getNodeName().equals("defeater")) {
                res.setDefeater(getSentence(n));
            }
            //Get next sibling node
            n = n.getNextSibling();
        }
        return res;
    }

    InferenceRule getInferenceRule(Node node) {
        InferenceRule res = new InferenceRule();
        Node n = node.getFirstChild();
        while (n != null) {
            if (n.getNodeName().equals("premise")) {
                res.addPremise(getSentence(n));
            } else if (n.getNodeName().equals("conclusion")) {
                res.setConclusion(getSentence(n));
            }
            //Get next sibling node
            n = n.getNextSibling();
        }
        return res;
    }

    void addBelief(Node node) {
        Node n = node.getFirstChild();
        Agent agent = null;
        Sentence fact = null;
        InferenceRule rule = null;
        DefeatRule defeat_rule = null;
        Measure measure = null;
        boolean bi_direct = false;
//        double level = 0;
        while (n != null) {
            if (n.getNodeName().equals("agent")) {
                agent = getAgent(n);
            } else if (n.getNodeName().equals("fact")) {
                fact = getFact(n);
            } else if (n.getNodeName().equals("rule")) {
                rule = getInferenceRule(n);
            } else if (n.getNodeName().equals("defeatrule")) {
                defeat_rule = getDefeatRule(n);
                String di = (getAttribute(n, "direction"));
                if (di != null )
                {
                    //bi-direction
                    if (di.equals("bi"))
                    {
                        bi_direct = true;
                    }
                }

            } else //                if (
            //                    n.getNodeName().equals("level")
            //                    || n.getNodeName().equals("creptprop")
            //                    )
            {
                Measure m = getMeasure(n);
                if (m != null) {
                    measure = m;
                }
            }


            //Get next sibling node
            n = n.getNextSibling();
        }
        if (fact != null) {
            mBeliefBase.believe(agent, fact, measure);

        } else if (rule != null) {
            mBeliefBase.believe(agent, rule, measure);
        } else if (defeat_rule != null) {
            _DefeatRuleList.add(defeat_rule);
            if (bi_direct == true)
            {
                DefeatRule inv_defeat = new DefeatRule();
                inv_defeat.setDefeatee(defeat_rule.getDefeater());
                inv_defeat.setDefeater(defeat_rule.getDefeatee());
                _DefeatRuleList.add(inv_defeat);
            }
        }

    }
    Agent mQueryAgent;
    List<Sentence> mQuestionList = new ArrayList<Sentence>();

    public List<Sentence> getQuestionLst() {
        if (mQuestionList.isEmpty()) {
            return null;
        } else {
            return mQuestionList;
        }
    }

    public Agent getQueryAgent() {
        return mQueryAgent;
    }

    void getQuery(Node node) {
        Node n = node.getFirstChild();

        while (n != null) {
            if (n.getNodeName().equals("agent")) {
                String agent_name = getNodeTextValue(n);
                if (!mTrustnet.hasAgent(agent_name)) {
                    System.out.println("Query agent \"" + agent_name + "\" is not in the trustnet.");
                }
                mQueryAgent = getAgent(n);
            } else if (n.getNodeName().equals("question")) {
                mQuestionList.add(getSentence(n));
            }
            //Get next sibling node
            n = n.getNextSibling();
        }

    }

    public void process(Node node) {

        if (node.getNodeName().equals("argtrust")) {
            //root
            Node n = node.getFirstChild();
            while (n != null) {
                process(n);
                //Get next sibling node
                n = n.getNextSibling();
            }

        } else if (node.getNodeName().equals("domain")) {
            Node n = node.getFirstChild();
            while (n != null) {
                if (n.getNodeName().equals("constant")) {
                    addConstant(n);
                } else if (n.getNodeName().equals("predicate")) {
                    addPredicate(n);
                }
                //Get next sibling node
                n = n.getNextSibling();
            }
        } else if (node.getNodeName().equals("trustnet")) {
            Node n = node.getFirstChild();
            while (n != null) {
                if (n.getNodeName().equals("agent")) {
                    getAgent(n);
                } else if (n.getNodeName().equals("trust")) {
                    addTrust(n);
                }
                //Get next sibling node
                n = n.getNextSibling();
            }

        } else if (node.getNodeName().equals("beliefbase")) {
            Node n = node.getFirstChild();
            while (n != null) {
                if (n.getNodeName().equals("belief")) {
                    addBelief(n);
                }
                //Get next sibling node
                n = n.getNextSibling();
            }

            } else if (node.getNodeName().equals("include")) {
            Node n = node.getFirstChild();
            while (n != null) {
                if (n.getNodeName().equals("file")) {
                    processIncludeFile(n);
                }
                //Get next sibling node
                n = n.getNextSibling();
            }

        } else if (node.getNodeName().equals("query")) {
            getQuery(node);
        } else { //Default: recursively processing the nodes
//            System.err.println("Unsupported node:");
//            print(node);
            Node n = node.getFirstChild();
            while (n != null) {
                process(n);
                //Get next sibling node
                n = n.getNextSibling();
            }
        }
    }

    public void print(Node node) {

        System.out.println(node.getNodeName()
                + ": " + this.getAttribute(node, "id")
                //                + "= " + node.getNodeValue()
                + "= " + this.getNodeTextValue(node));
    }

    //------------Helper functions ----------------
    public String getAttribute(Node node, String name) {
        NamedNodeMap attributes = (NamedNodeMap) node.getAttributes();
        if (attributes == null) {
            return null;
        }
        Node attribute = attributes.getNamedItem(name);
        if (attribute == null) {
            return null;
        }
        return attribute.getNodeValue().trim();

    }

    public String getNodeTextValue(Node node) {
        NodeList nlList = node.getChildNodes();
        Node nValue = (Node) nlList.item(0);
        if (nValue == null) {
            return null;
        }
        return nValue.getNodeValue().trim();
    }

//    public String getAttribute(Node node, String name) {
//        if (node instanceof Element) {
//            Element node_element = (Element) node;
//            if (node_element.hasAttribute(name)) {
//                String attr_value = node_element.getAttribute(name);
//                return attr_value.trim();
//            } else {
//                return null;
//            }
//
//        } else {
//            return null;
//        }
//    }
//    public String getTagValue(String sTag, Element eElement) {
//        NodeList nlList =
//                eElement.getElementsByTagName(sTag).item(0).getChildNodes();
//        Node nValue = (Node) nlList.item(0);
//
//        return nValue.getNodeValue().trim();
//    }
//    public static void main(String[] args) {
//        //Create FOL mDomain:
//        FOLDomain domain = new FOLDomain();
//        domain.addConstant("hce");
//        domain.addConstant("almodovar");
//        domain.addPredicate("IndieFilm");
//        domain.addPredicate("SpanishFilm");
//        domain.addPredicate("DirectedBy");
//        domain.addPredicate("Comedy");
//        domain.addPredicate("Watch");
//
//        //TrustNetwork:
//        TrustNet trust_net = new TrustNet();
//        //Agent:
//        Agent john = trust_net.getAgent("john");
//        Agent alice = trust_net.getAgent("alice");
//        Agent dave = trust_net.getAgent("dave");
//        Agent jane = trust_net.getAgent("jane");
//        Agent mary = trust_net.getAgent("mary");
//
//        trust_net.trust(john, mary, 0.9);
//        trust_net.trust(mary, jane, 0.7);
//        trust_net.trust(mary, dave, 0.8);
//        trust_net.trust(alice, dave, 0.4);
//        trust_net.trust(alice, jane, 0.6);
//
//
//        //Create FOL sentences and inference rules
//        FOLParser fol = new FOLParser(domain);
//
//
//        //Belief base:
//        BeliefBase bb = new BeliefBase();
//        bb.believe(john, fol.parse("IndieFilm(hce)"), 1);
//        bb.believe(john, fol.parse("SpanishFilm(hce)"), 1);
//        bb.believe(john, fol.parse("DirectedBy(hce, almodovar)"), 1);
//
//        bb.believe(john,
//                (new InferenceRule()).P_(fol.parse("Comedy(x)")).C_(fol.parse("NOT Watch(x)")),
//                1);
//
//        bb.believe(jane,
//                (new InferenceRule()).P_(fol.parse("IndieFilm(x)")).P_(fol.parse("SpanishFilm(x)")).C_(fol.parse("NOT Watch(x)")),
//                1);
//
//        bb.believe(dave,
//                (new InferenceRule()).P_(fol.parse("IndieFilm(x)")).P_(fol.parse("DirectedBy(x, almodovar)")).C_(fol.parse("Watch(x)")),
//                1);
//
//        bb.believe(john,
//                new InferenceRule().P_(fol.parse("DirectedBy(x, almodovar)")).C_(fol.parse("NOT IndieFilm(x)")),
//                1);
////        bb.believe(john,
////                new InferenceRule().P_(mFolParser.parse("NOT IndieFilm(x)")).C_(mFolParser.parse("Watch(x)")),
////                1);
//        bb.believe(mary,
//                fol.parse("IndieFilm(hce)"),
//                1);
////        bb.believe(john, mFolParser.parse("Comedy(hce)"), 1);
//
//
//        //Argumentation system:
//        FOLKnowledgeBase kb = new FOLKnowledgeBase();
//        kb.acceptKnowledgeBase(bb.getFactSet(), bb.getRuleSet());
//        ArgumentationSystem arg_sys = new ArgumentationSystem(kb);
////        FOLNaturalDeduction deduct_sys = mArgSys.getDeductionSys();
////        InferenceNode inf = deduct_sys.getInferenceNet(mFolParser.parse("NOT Watch(hce)"));
////        while (inf != null) {
////            System.out.println("found: ");
////            inf.debug_print(System.out);
////            inf = deduct_sys.getNextInferenceNet(inf);
////        }
//        ArguingNode watch_hce_arggraph = arg_sys.getArgGraph(fol.parse("Watch(hce)"));
////        ArguingNode not_watch_hce_arggraph =
////        mArgSys.getArgGraph(mFolParser.parse("NOT Watch(hce)"));
//
//        watch_hce_arggraph.debug_print(System.out);
//
//        ArgTrustGraph gv = new ArgTrustGraph();
//        gv.setArgSys(arg_sys);
//        gv.setBeliefBase(bb);
//        gv.setTrustNet(trust_net);
//        ITrustSemantics trust_sem = new TidalTrust(trust_net, 0);
//        gv.setTrustSemantics(trust_sem);
//
//        gv.setTrustBeliefSemantics(new TidalTrustBelief(bb, trust_sem));
//        gv.openFile("/home/tang/temp/jcl.dot");
//        gv.draw(watch_hce_arggraph, trust_net.getTrustNode(john));
////        gv.draw(mArgSys.getAllNodes(), mTrustnet.getTrustNodes());
//        gv.close();
//
//    }
}
